iT邦幫忙

2022 iThome 鐵人賽

DAY 6
0
Software Development

燃燒大三的成果發表系列 第 6

燃燒大三的成果發表第六天 - 方法(Method)

  • 分享至 

  • xImage
  •  

今天我要來分享方法(Method)的概念,方法有分三種:

  • 實例方法(Instance Method)
  • 類別方法(Class Method)
  • 靜態方法(Static Method)

實體方法(Instance Method)

我將昨日的程式碼做一個小小的修改,我將所有的需要引入的參數都刪除,只留下了self,讓我們看一下底下這段程式碼:

class Person(object):

    def introduce_myself(self):
        print("Hi, I am {self} ".format(self=self))

if __name__ == '__main__':
    Jacky = Person()
    Jacky.introduce_myself() #Hi, I am <__main__.Person object at 0x10cb271d0>

你是否不禁好奇,為什麼留了一個「self」的參數呢?讓我們試著把這個self印出一探究竟,我們可以發現這個self印出的是Personal的物件以及記憶體位置。

我將程式碼在做一個小小的修改,如果我們沒有先實例化一個物件會變成怎樣呢?,讓我們看一下底下這段程式碼:

class Person(object):

    def introduce_myself(self):
        print("Hi, I am {self} ".format(self=self))
        self.ten_years_later()
    
    def ten_years_later(self):
        print("Hi, I am {self} ".format(self=self))


if __name__ == '__main__':
    # 有先實例化物件
    Jacky = Person()
    Jacky.introduce_myself() #Hi, I am <__main__.Person object at 0x100bd7278> *2
    Sandy = Person()
    Sandy.introduce_myself() #Hi, I am <__main__.Person object at 0x100bd72b0> *2
    
    # 沒有實例化物件
    Person.introduce_myself() #TypeError: introduce_myself() missing 1 required positional argument: 'self'

從15行我們可以發現,如果我們沒有實例化的話,會出現

TypeError: introduce_myself() missing 1 required positional argument: 'self'

的錯誤,這個錯誤是說缺少一個self的參數,也就是説在呼叫方法的同時,這個物件會作為參數傳入方法,所以一定必須得要先實例化一個物件才能夠呼叫方法,這也就是我們所說的實體方法。

第5行我們能看到因為實體方法是用把實例代入參數裡面,所以Jacky & Sandy這兩個「物件」的所有方法,都能夠透過「self」互相呼叫非常的方便。最後我們可以發現實例方法的英文裡面有「Instance」,從英文上去理解能夠比較好理解,其實實體方法就是實例(Instance) 的方法(Method),底下要介紹的類別方法也是一樣的概念。

類別方法(Class Method)

其實每一種方法,都很相似,我們馬上來看看類別方法例子的程式碼:

class Person(object):

    @classmethod
    def jacky_introduce_myself(cls):
        print("Hi, I am Jacky {cls} ".format(cls=cls)) # Hi, I am Jacky <class '__main__.Person'>
        cls.sandy_introduce_myself()
    
    @classmethod
    def sandy_introduce_myself(cls):
        print("Hi, I am Sandy {cls} ".format(cls=cls)) # Hi, I am Sandy <class '__main__.Person'> 



if __name__ == '__main__':
    # 有先實例化物件
    Jacky = Person()
    Jacky.jacky_introduce_myself()
    print(Jacky) # <__main__.Person object at 0x106ad12e8>

    # 沒有實例化物件
    Person.jacky_introduce_myself() # Hi, I am Jacky <class '__main__.Person'> & Hi, I am Sandy <class '__main__.Person'>

我在兩個方法上面各加上一行@classmethod這段程式碼使他變成類別方法,然後把「self」換成「cls」然後接著印出,我們可以發現「cls指向的是Person這個類別」,而不是像實體方法「self是指向類別實例化後的物件」,所以我們在第6行可以發現,Person這個類別也能透過「cls」互相呼叫,然後類別方法無論有沒有實例化物件都是能夠呼叫。

靜態方法(Static Method)

接著我們要來介紹靜態方法,一樣很快地看到我們的範例程式碼:

class Person(object):

    @staticmethod
    def jacky_introduce_myself():
        print("Hi, I am Jacky ") # Hi, I am Jacky
        Person.sandy_introduce_myself()
    
    @staticmethod
    def sandy_introduce_myself():
        print("Hi, I am Sandy ") # Hi, I am Sandy 



if __name__ == '__main__':
    # 有先實例化物件
    Jacky = Person()
    Jacky.jacky_introduce_myself()
    print(Jacky) # <__main__.Person object at 0x106ad12e8>

    # 沒有實例化物件
    Person.jacky_introduce_myself() # Hi, I am Jacky & Hi, I am Sandy


靜態方法的範例程式碼跟類別的也太像了,只差在參數「cls」刪掉以及@classmethod改成了@staticmethod,看似簡單的動作,少了cls這個參數雖然一樣能夠呼叫類別底下的方法,但是在懂的人眼中架構卻是大大的不一樣。靜態方法更像是一個很單純的工具,沒有物件的架構,只是可能常常會被別人拿來用,所以宣告靜態的方式,不需要其他的東西。

函數與方法的差別

最後總結之前我來提一下函式和方法的差別,「函式只是程式碼組成的區塊,他可以寫在任何地方,無論是類別裡面或者是類別外面都可以」,但是方法就不一樣了,「方法都是基於類別,有一個隱性、預設的參數(self or cls),如果你不是寫在類別裡面,呼叫方法的時候,一樣會有缺少self or 缺少cls 參數的錯誤」。

題外話這些參數可以不使用這些名稱(self or cls),但是因為大家使用上這樣稱呼,也容易看懂,久而久之就習慣成自然了。

今日總結

最後來個總結一下今天介紹的三種方法,雖然每種方法都有用有實體物件與沒有實體物件,但是實際上只要記得實體物件對實體方法十分地重要,另外兩種方法可以不需要。

實體方法(Instance Method) 類別方法(Class Method) 靜態方法(Static Method)
呼叫方式 實體物件 類別 類別
傳入參數 self cls

明日我會把今日分享的方法(Method)搭配屬性(Attribute) 做一個更完整的說明。


上一篇
燃燒大三的成果發表第五天 - 類別(Class)、建構子(Construct)
下一篇
燃燒大三的成果發表第七天 - 屬性(Attributes)
系列文
燃燒大三的成果發表30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言